//-------------------------------------------------------------------
// Filename: basic_rf.c
// Description: Basic RF library
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// INCLUDES
//-------------------------------------------------------------------
#include "hal_defs.h"
#include "hal_int.h"
#include "hal_mcu.h"            // Using halMcuWaitUs()
#include "hal_rf.h"
#include "basic_rf.h"
#ifdef SECURITY_CCM
    #include "hal_rf_security.h"
    #include "basic_rf_security.h"
#endif 
#include <string.h>

//-------------------------------------------------------------------
// CONSTANTS AND DEFINES
//-------------------------------------------------------------------
// Packet and packet part lengths
#define PKT_LEN_MIC                         8
#define PKT_LEN_SEC                         PKT_LEN_UNSEC + PKT_LEN_MIC
#define PKT_LEN_AUTH                        8
#define PKT_LEN_ENCR                        24

// Packet overhead ((frame control field, sequence number, PAN ID,
// destination and source) + (footer))
// Note that the length byte itself is not included included in the packet length
#define BASIC_RF_PACKET_OVERHEAD_SIZE       ((2 + 1 + 2 + 2 + 2) + (2))
#define BASIC_RF_MAX_PAYLOAD_SIZE	        (127 - BASIC_RF_PACKET_OVERHEAD_SIZE - BASIC_RF_AUX_HDR_LENGTH - BASIC_RF_LEN_MIC)
#define BASIC_RF_ACK_PACKET_SIZE	        5
#define BASIC_RF_FOOTER_SIZE                2
#define BASIC_RF_HDR_SIZE                   10

// The time it takes for the acknowledgment packet to be received after the
// data packet has been transmitted.
#define BASIC_RF_ACK_DURATION		        (0.5 * 32 * 2 * ((4 + 1) + (1) + (2 + 1) + (2)))
#define BASIC_RF_SYMBOL_DURATION	        (32 * 0.5)

// The length byte
#define BASIC_RF_PLD_LEN_MASK               0x7F

// Frame control field
#define BASIC_RF_FCF_NOACK                  0x8841
#define BASIC_RF_FCF_ACK                    0x8861
#define BASIC_RF_FCF_ACK_BM                 0x0020
#define BASIC_RF_FCF_BM                     (~BASIC_RF_FCF_ACK_BM)
#define BASIC_RF_SEC_ENABLED_FCF_BM         0x0008

// Frame control field LSB
#define BASIC_RF_FCF_NOACK_L                LO_UINT16(BASIC_RF_FCF_NOACK)
#define BASIC_RF_FCF_ACK_L                  LO_UINT16(BASIC_RF_FCF_ACK)
#define BASIC_RF_FCF_ACK_BM_L               LO_UINT16(BASIC_RF_FCF_ACK_BM)
#define BASIC_RF_FCF_BM_L                   LO_UINT16(BASIC_RF_FCF_BM)
#define BASIC_RF_SEC_ENABLED_FCF_BM_L       LO_UINT16(BASIC_RF_SEC_ENABLED_FCF_BM)

// Auxiliary Security header
#define BASIC_RF_AUX_HDR_LENGTH             5
#define BASIC_RF_LEN_AUTH                   BASIC_RF_PACKET_OVERHEAD_SIZE + BASIC_RF_AUX_HDR_LENGTH - BASIC_RF_FOOTER_SIZE
#define BASIC_RF_SECURITY_M                 2
#define BASIC_RF_LEN_MIC                    8
#ifdef SECURITY_CCM
    #undef BASIC_RF_HDR_SIZE
    #define BASIC_RF_HDR_SIZE                   15
#endif 

// Footer
#define BASIC_RF_CRC_OK_BM                  0x80


//-------------------------------------------------------------------
// TYPEDEFS
//-------------------------------------------------------------------
// The receive struct
typedef struct
{
    uint8 seqNumber;
    uint16 srcAddr;
    uint16 srcPanId;
    int8 length;
    uint8 *pPayload;
    uint8 ackRequest;
    int8 rssi;
    volatile uint8 isReady;
    uint8 status;
} basicRfRxInfo_t;

// Tx state
typedef struct
{
    uint8 txSeqNumber;
    volatile uint8 ackReceived;
    uint8 receiveOn;
    uint32 frameCounter;
} basicRfTxState_t;


// Basic RF packet header (IEEE 802.15.4)
typedef struct
{
    uint8 packetLength;
    uint8 fcf0; // Frame control field LSB
    uint8 fcf1; // Frame control field MSB
    uint8 seqNumber;
    uint16 panId;
    uint16 destAddr;
    uint16 srcAddr;
    #ifdef SECURITY_CCM
        uint8 securityControl;
        uint8 frameCounter[4];
    #endif 
} basicRfPktHdr_t;

//-------------------------------------------------------------------
// LOCAL VARIABLES
//-------------------------------------------------------------------
static basicRfRxInfo_t rxi = 
{
    0xFF
}; // Make sure sequence numbers are
static basicRfTxState_t txState = 
{
    0x00
}; // initialised and distinct.

static basicRfCfg_t *pConfig;
static uint8 txMpdu[BASIC_RF_MAX_PAYLOAD_SIZE + BASIC_RF_PACKET_OVERHEAD_SIZE + 1];
static uint8 rxMpdu[128];

//-------------------------------------------------------------------
// LOCAL FUNCTIONS
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// @fn          basicRfBuildHeader
// @brief       Builds packet header according to IEEE 802.15.4 frame format
// @param       buffer - Pointer to buffer to write the header
//              destAddr - destination short address
//              payloadLength - length of higher layer payload
// @return      uint8 - length of header
//-------------------------------------------------------------------
static uint8 basicRfBuildHeader(uint8 *buffer, uint16 destAddr, uint8 payloadLength)
{
    basicRfPktHdr_t *pHdr;
    uint16 fcf;

    pHdr = (basicRfPktHdr_t*)buffer;

    // Populate packet header
    pHdr->packetLength = payloadLength + BASIC_RF_PACKET_OVERHEAD_SIZE;
    //pHdr->frameControlField = pConfig->ackRequest ? BASIC_RF_FCF_ACK : BASIC_RF_FCF_NOACK;
    fcf = pConfig->ackRequest ? BASIC_RF_FCF_ACK : BASIC_RF_FCF_NOACK;
    pHdr->fcf0 = LO_UINT16(fcf);
    pHdr->fcf1 = HI_UINT16(fcf);
    pHdr->seqNumber = txState.txSeqNumber;
    pHdr->panId = pConfig->panId;
    pHdr->destAddr = destAddr;
    pHdr->srcAddr = pConfig->myAddr;

    #ifdef SECURITY_CCM

        // Add security to FCF, length and security header
        pHdr->fcf0 |= BASIC_RF_SEC_ENABLED_FCF_BM_L;
        pHdr->packetLength += PKT_LEN_MIC;
        pHdr->packetLength += BASIC_RF_AUX_HDR_LENGTH;

        pHdr->securityControl = SECURITY_CONTROL;
        pHdr->frameCounter[0] = LO_UINT16(LO_UINT32(txState.frameCounter));
        pHdr->frameCounter[1] = HI_UINT16(LO_UINT32(txState.frameCounter));
        pHdr->frameCounter[2] = LO_UINT16(HI_UINT32(txState.frameCounter));
        pHdr->frameCounter[3] = HI_UINT16(HI_UINT32(txState.frameCounter));

    #endif 

    return BASIC_RF_HDR_SIZE;
}

//-------------------------------------------------------------------
// @fn          basicRfBuildMpdu
// @brief       Builds mpdu (MAC header + payload) according to IEEE 802.15.4
//              frame format
// @param       destAddr - Destination short address
//              pPayload - pointer to buffer with payload
//              payloadLength - length of payload buffer
// @return      uint8 - length of mpdu
//-------------------------------------------------------------------
static uint8 basicRfBuildMpdu(uint16 destAddr, uint8 *pPayload, uint8 payloadLength)
{
    uint8 hdrLength, n;

    hdrLength = basicRfBuildHeader(txMpdu, destAddr, payloadLength);

    for (n = 0; n < payloadLength; n++)
    {
        txMpdu[hdrLength + n] = pPayload[n];
    }
    return hdrLength + payloadLength; // total mpdu length
}

//-------------------------------------------------------------------
// @fn          basicRfRxFrmDoneIsr
// @brief       Interrupt service routine for received frame from radio
//              (either data or acknowlegdement)
// @param       rxi - file scope variable info extracted from the last incoming
//                    frame
//             txState - file scope variable that keeps tx state info
// @return      none
//-------------------------------------------------------------------
static void basicRfRxFrmDoneIsr(void)
{
    basicRfPktHdr_t *pHdr;
    uint8 *pStatusWord;
    #ifdef SECURITY_CCM
        uint8 authStatus = 0;
    #endif 

    // Map header to packet buffer
    pHdr = (basicRfPktHdr_t*)rxMpdu;

    // Clear interrupt and disable new RX frame done interrupt
    halRfDisableRxInterrupt();

    // Enable all other interrupt sources (enables interrupt nesting)
    halIntOn();

    // Read payload length.
    halRfReadRxBuf(&pHdr->packetLength, 1);
    pHdr->packetLength &= BASIC_RF_PLD_LEN_MASK; // Ignore MSB

    // Is this an acknowledgment packet?
    // Only ack packets may be 5 bytes in total.
    if (pHdr->packetLength == BASIC_RF_ACK_PACKET_SIZE)
    {
        // Read the packet
        halRfReadRxBuf(&rxMpdu[1], pHdr->packetLength);

        rxi.ackRequest = !!(pHdr->fcf0 &BASIC_RF_FCF_ACK_BM_L);

        // Read the status word and check for CRC OK
        pStatusWord = rxMpdu + 4;

        // Indicate the successful ACK reception if CRC and sequence number OK
        if ((pStatusWord[1] &BASIC_RF_CRC_OK_BM) && (pHdr->seqNumber == txState.txSeqNumber))
        {
            txState.ackReceived = TRUE;
        }
        // No, it is data
    }
    else
    {
        // It is assumed that the radio rejects packets with invalid length.
        // Subtract the number of bytes in the frame overhead to get actual payload.
        rxi.length = pHdr->packetLength - BASIC_RF_PACKET_OVERHEAD_SIZE;

        #ifdef SECURITY_CCM
            rxi.length -= (BASIC_RF_AUX_HDR_LENGTH + BASIC_RF_LEN_MIC);
            authStatus = halRfReadRxBufSecure(&rxMpdu[1], pHdr->packetLength, rxi.length, BASIC_RF_LEN_AUTH, BASIC_RF_SECURITY_M);
        #else 
            halRfReadRxBuf(&rxMpdu[1], pHdr->packetLength);
        #endif 

        rxi.ackRequest = !!(pHdr->fcf0 &BASIC_RF_FCF_ACK_BM_L);

        // Read the source address
        rxi.srcAddr = pHdr->srcAddr;

        // Read the packet payload
        rxi.pPayload = rxMpdu + BASIC_RF_HDR_SIZE;

        // Read the FCS to get the RSSI and CRC
        pStatusWord = rxi.pPayload + rxi.length;
        #ifdef SECURITY_CCM
            pStatusWord += BASIC_RF_LEN_MIC;
        #endif 
        rxi.rssi = pStatusWord[0];

        // Notify the application about the received data packet if the CRC is OK
        // Throw packet if the previous packet had the same sequence number
        if ((pStatusWord[1] &BASIC_RF_CRC_OK_BM) )//&& (rxi.seqNumber != pHdr->seqNumber))
        {
            // If security is used check also that authentication passed
            #ifdef SECURITY_CCM
                if (authStatus == SUCCESS)
                {
                    if ((pHdr->fcf0 &BASIC_RF_FCF_BM_L) == (BASIC_RF_FCF_NOACK_L | BASIC_RF_SEC_ENABLED_FCF_BM_L))
                    {
                        rxi.isReady = TRUE;
                    }
                }
            #else 
                if (((pHdr->fcf0 &(BASIC_RF_FCF_BM_L)) == BASIC_RF_FCF_NOACK_L))
                {
                    rxi.isReady = TRUE;
                }
            #endif 
        }
        rxi.seqNumber = pHdr->seqNumber;
    }
    // Enable RX frame done interrupt again
    halIntOff();
    halRfEnableRxInterrupt();
}

//-------------------------------------------------------------------
// GLOBAL FUNCTIONS
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// @fn          basicRfInit
// @brief       Initialise basic RF datastructures. Sets channel, short address and
//             PAN id in the chip and configures interrupt on packet reception
// @param       pRfConfig - pointer to BASIC_RF_CONFIG struct.
//                          This struct must be allocated by higher layer
//              txState - file scope variable that keeps tx state info
//              rxi - file scope variable info extracted from the last incoming frame
// @return      none
//-------------------------------------------------------------------
uint8 basicRfInit(basicRfCfg_t *pRfConfig)
{
    if (halRfInit() == FAILED)
    {
        return FAILED;
    }

    halIntOff();

    // Set the protocol configuration
    pConfig = pRfConfig;
    rxi.pPayload = NULL;

    txState.receiveOn = TRUE;
    txState.frameCounter = 0;

    // Set channel
    halRfSetChannel(pConfig->channel);

    // Write the short address and the PAN ID to the CC2520 RAM
    halRfSetShortAddr(pConfig->myAddr);
    halRfSetPanId(pConfig->panId);

    // if security is enabled, write key and nonce
    #ifdef SECURITY_CCM
        basicRfSecurityInit(pConfig);
    #endif 

    // Set up receive interrupt (received data or acknowlegment)
    halRfRxInterruptConfig(basicRfRxFrmDoneIsr);

    halIntOn();

    return SUCCESS;
}

//-------------------------------------------------------------------
// @fn          basicRfSendPacket
// @brief       Send packet
// @param       destAddr - destination short address
//              pPayload - pointer to payload buffer. This buffer must be
//                         allocated by higher layer.
//             length - length of payload
//              txState - file scope variable that keeps tx state info
//              mpdu - file scope variable. Buffer for the frame to send
// @return      basicRFStatus_t - SUCCESS or FAILED
//-------------------------------------------------------------------
uint8 basicRfSendPacket(uint16 destAddr, uint8 *pPayload, uint8 length)
{
    uint8 mpduLength;
    uint8 status;

    // Turn on receiver if its not on
    if (!txState.receiveOn)
    {
        halRfReceiveOn();
    }

    // Check packet length
    length = MIN(length, BASIC_RF_MAX_PAYLOAD_SIZE);

    // Wait until the transceiver is idle
    halRfWaitTransceiverReady();

    // Turn off RX frame done interrupt to avoid interference on the SPI interface
    halRfDisableRxInterrupt();

    mpduLength = basicRfBuildMpdu(destAddr, pPayload, length);

    #ifdef SECURITY_CCM
        halRfWriteTxBufSecure(txMpdu, mpduLength, length, BASIC_RF_LEN_AUTH, BASIC_RF_SECURITY_M);
        txState.frameCounter++; // Increment frame counter field
    #else 
        halRfWriteTxBuf(txMpdu, mpduLength);
    #endif 

    // Turn on RX frame done interrupt for ACK reception
    halRfEnableRxInterrupt();

    // Send frame with CCA. return FAILED if not successful
    if (halRfTransmit() != SUCCESS)
    {
        status = FAILED;
    }

    // Wait for the acknowledge to be received, if any
    if (pConfig->ackRequest)
    {
        txState.ackReceived = FALSE;

        // We'll enter RX automatically, so just wait until we can be sure that the ack reception should have finished
        // The timeout consists of a 12-symbol turnaround time, the ack packet duration, and a small margin
        halMcuWaitUs((12 *BASIC_RF_SYMBOL_DURATION) + (BASIC_RF_ACK_DURATION) + (2 *BASIC_RF_SYMBOL_DURATION) + 10);

        // If an acknowledgment has been received (by RxFrmDoneIsr), the ackReceived flag should be set
        status = txState.ackReceived ? SUCCESS : FAILED;
    }
    else
    {
        status = SUCCESS;
    }

    // Turn off the receiver if it should not continue to be enabled
    if (!txState.receiveOn)
    {
        halRfReceiveOff();
    }

    if (status == SUCCESS)
    {
        txState.txSeqNumber++;
    }

    #ifdef SECURITY_CCM
        halRfIncNonceTx(); // Increment nonce value
    #endif 

    return status;
}

//-------------------------------------------------------------------
// @fn          basicRfPacketIsReady
// @brief       Check if a new packet is ready to be read by next higher layer
// @param       none
// @return      uint8 - TRUE if a packet is ready to be read by higher layer
//-------------------------------------------------------------------
uint8 basicRfPacketIsReady(void)
{
    return rxi.isReady;
}

//-------------------------------------------------------------------
// @fn          basicRfReceive
// @brief       Copies the payload of the last incoming packet into a buffer
// @param       pRxData - pointer to data buffer to fill. This buffer must be
//                        allocated by higher layer.
//              len - Number of bytes to read in to buffer
//              rxi - file scope variable holding the information of the last
//                    incoming packet
// @return      uint8 - number of bytes actually copied into buffer
//-------------------------------------------------------------------
uint8 basicRfReceive(uint8 *pRxData, uint16 len, int16 *pRssi)
{
    // Accessing shared variables -> this is a critical region
    // Critical region start
    halIntOff();
    memcpy(pRxData, rxi.pPayload, MIN(rxi.length, len));
    if (pRssi != NULL)  
    {
        if (rxi.rssi < 128)
        {
            *pRssi = rxi.rssi - halRfGetRssiOffset();
        }
        else
        {
            *pRssi = (rxi.rssi - 256) - halRfGetRssiOffset();
        }
    }
    rxi.isReady = FALSE;
    halIntOn();

    // Critical region end
    return MIN(rxi.length, len);
}

//-------------------------------------------------------------------
// @fn          basicRfGetRssi
// @brief       Copies the payload of the last incoming packet into a buffer
// @param       none
// @return      int8 - RSSI value
//-------------------------------------------------------------------
int8 basicRfGetRssi(void)
{
    if (rxi.rssi < 128)
    {
        return rxi.rssi - halRfGetRssiOffset();
    }
    else
    {
        return (rxi.rssi - 256) - halRfGetRssiOffset();
    }
}

//-------------------------------------------------------------------
// @fn          basicRfReceiveOn
// @brief       Turns on receiver on radio
// @param       txState - file scope variable
// @return      none
//-------------------------------------------------------------------
void basicRfReceiveOn(void)
{
    txState.receiveOn = TRUE;
    halRfReceiveOn();
}

//-------------------------------------------------------------------
// @fn          basicRfReceiveOff
// @brief       Turns off receiver on radio
// @param       txState - file scope variable
// @return      none
//-------------------------------------------------------------------
void basicRfReceiveOff(void)
{
    txState.receiveOn = FALSE;
    halRfReceiveOff();
}
